#include <gfx/projector.h>


ceProjector::ceProjector()
  : _perspective(true)
{
}

void ceProjector::SetPerspective(bool perspective)
{
  _perspective = perspective;
}

void ceProjector::SetOrtho(bool ortho)
{
  _perspective = !ortho;
}

void ceProjector::FinishTransformation()
{
  if (_perspective)
    {
      _matrix.SetPerspective(_left, _right, _bottom, _top, _near, _far);
    }
  else
    {
      _matrix.SetOrtho(_left, _right, _bottom, _top, _near, _far);
    }
}

void ceProjector::SetOrtho2D(unsigned width, unsigned height)
{
  SetOrtho();
  _left = 0;
  _right = width;

  _bottom = 0;
  _top = height;

  _near = -10.0;
  _far = 10.0;

  FinishTransformation();
}

const ceMatrix4f& ceProjector::GetMatrix() const
{
  return _matrix;
}

void ceProjector::SetLeft(float left)
{
  _left = left;
}

float ceProjector::GetLeft() const
{
  return _left;
}

void ceProjector::SetRight(float right)
{
  _right = right;
}

float ceProjector::GetRight() const
{
  return _right;
}

void ceProjector::SetBottom(float bottom)
{
  _bottom = bottom;
}

float ceProjector::GetBottom() const
{
  return _bottom;
}

void ceProjector::SetTop(float top)
{
  _top = top;
}

float ceProjector::GetTop() const
{
  return _top;
}

void ceProjector::SetNear(float near)
{
  _near = near;
}

float ceProjector::GetNear() const
{
  return _near;
}

void ceProjector::SetFar(float far)
{
  _far = far;
}

float ceProjector::GetFar() const
{
  return _far;
}


ceVector3f ceProjector::Get(float x, float y) const
{
  float rx = (x + 1.0f) / 2.0f;
  float ry = (y + 1.0f) / 2.0f;

  return ceVector3f (_left   + rx * (_right - _left),
                     _bottom + ry * (_top - _bottom),
                     -_near);
}

float ceProjector::GetSphereRadius (float distance, bool min) const
{
#define MIN(x,y) ((x)<(y)?(x):(y))
#define MAX(x,y) ((x)>(y)?(x):(y))
#define ABS(v) if (v < 0.0f) v = -v
	if (!_perspective)
		{
			float h = (_right - _left) / 2.0f;
			float v = (_top - _bottom) / 2.0f;
			if (min)
				{
					return MIN(h,v);
				}
			else
				{
					return MAX(h,v);
				}
		}
	else
		{
			float d = distance / _near;
			float t = _top * d;
			float b = _bottom * d;
			float l = _left * d;
			float r = _right * d;
			ABS(t);ABS(b);ABS(l);ABS(r);
			if (min)
				{
					return MIN(MIN(t,b),MIN(l,r));
				}
			else
				{
					return MAX(MAX(t,b),MAX(l,r));
				}
		}
#undef MIN
#undef MAX
}
